package net.lulihu.common_util.jdbc.template;

import net.lulihu.Assert;
import net.lulihu.ObjectKit.BeanKit;
import net.lulihu.ObjectKit.KryoKit;
import net.lulihu.common_util.jdbc.annotation.Serializer;
import net.lulihu.common_util.jdbc.annotation.Table;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

abstract class SqlTemplate<K, V> {

    /**
     * sql模板缓存对象
     * <pre>
     *   Map<映射bean对象类型,Map<属性对象,映射操作描述>>
     * </pre>
     */
    private final Map<Class<?>, Map<K, V>> sqlTemplate;

    /**
     * bean对象上的注解缓存， Map<bean类型， Map<注解类型，注解对象>>
     */
    private final Map<Class<?>, Map<Class<? extends Annotation>, Annotation>> beanClassAnnotation;

    SqlTemplate() {
        sqlTemplate = new ConcurrentHashMap<>();
        beanClassAnnotation = new ConcurrentHashMap<>();
    }

    /**
     * 获取 对象指定类型注解
     *
     * @param beanClass 对象类型
     * @param annotate  指定注解类型
     * @param <T>       泛型
     * @return 注解类型
     */
    @SuppressWarnings("unchecked")
    <T extends Annotation> T getAnnotation(Class<?> beanClass, Class<T> annotate) {
        return (T) beanClassAnnotation.computeIfAbsent(beanClass, k -> new ConcurrentHashMap<>())
                .computeIfAbsent(annotate, beanClass::getAnnotation);
    }

    /**
     * 获取表名称注解
     *
     * @param beanClass 对象类型
     * @return Table
     */
    Table getTable(Class<?> beanClass) {
        Table table = getAnnotation(beanClass, Table.class);
        Assert.notNull(table, "对象【{}】未设置数据表映射，请使用@Table注解...", beanClass);
        return table;
    }

    /**
     * 是否进行序列化
     *
     * @param field 字段属性
     * @return true序列化 反之不序列化
     */
    boolean ifSerializer(Field field) {
        return field.getAnnotation(Serializer.class) != null;
    }

    /**
     * 序列化
     *
     * @param value 任意对象
     * @return 序列化后的值
     */
    Object serializer(Object value) {
        return KryoKit.writeToByteArray(value);
    }


    /**
     * 获取当前对象的所有列描述
     *
     * @param beanClass 对象类型
     * @return 对象类型描述
     */
    Map<K, V> getColumnDescriptor(Class<?> beanClass) {
        return sqlTemplate.computeIfAbsent(beanClass, k -> {
            // 获取所有字段
            List<Field> beanField = BeanKit.getBeanField(k);
            Map<K, V> columnDescriptorMap = fieldCache();
            // 循环生成每个模板对象的缓存映射
            for (Field field : beanField) {
                boolean hasContinue = hasContinue(field);
                if (hasContinue) continue;
                columnDescriptor(columnDescriptorMap, field);
            }
            return Collections.unmodifiableMap(columnDescriptorMap);
        });
    }


    /**
     * 是否跳过该字段
     *
     * @param field 属性对象
     * @return true 跳过反正不跳过
     */
    abstract boolean hasContinue(Field field);

    /**
     * 属性值缓存对象
     */
    abstract Map<K, V> fieldCache();

    /**
     * 字段描述
     *
     * @param fieldCache 字段缓存
     * @param field      字段对象
     */
    abstract void columnDescriptor(Map<K, V> fieldCache, Field field);

}
